#include "Col.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Region.h"
#include "Class.h"
#include "ET_stdio.h"

//---- Region ------------------------------------------------------------------

SimpleMetaImpl(Region)
{
	len= len;
	if (! isptr)
		sprintf(buf, "%d", ((Region*) addr)->Length());
}

Region::Region()
{
	buf= 0;
	cap= 0;
}

Region::Region(short *s, int len)
{
	if (s && len > 0) {
		buf= new short[cap= len];
		MemCpy(buf, s, cap*sizeof(short));
		CalcBBox();
	} else {
		cap= 0;
		buf= 0;
	}
}

Region::Region(const Rectangle &r) : bbox(r)
{
	buf= new short[cap= 7];
	register short *s= buf;
	*s++= 3; *s++= r.origin.y; *s++= r.origin.x; *s++= r.origin.x+r.extent.x;
	*s++= 1; *s++= r.origin.y+r.extent.y;
	*s= 0;
	CalcBBox();
}

Region::Region(const Region &r) : bbox(r.bbox)
{
	if (r.cap > 0) {
		buf= new short[cap= r.cap];
		MemCpy(buf, r.buf, cap*sizeof(short));
	} else {
		cap= 0;
		buf= 0;
	}
}

Region::~Region()
{
	SafeDelete(buf);
	cap= 0;
}

int Region::GetRects(Rectangle *rp) const
{
	register short *s= buf;
	int i= 0;

	if (s) {
		register int next;
		Rectangle r;
		while (next= *s++) {
			r.origin.y= *s++;
			r.extent.y= s[next] - r.origin.y;
			while (next > 1) {
				r.origin.x= *s++;
				r.extent.x= *s++-r.origin.x;
				next-= 2;
				if (r.IsNotEmpty())
					rp[i++]= r;
			}
		}
	}
	return i;
}

void Region::CalcBBox()
{
	register short *s= buf;
	register int next;
	Rectangle r;

	bbox= gRect0;
	if (s) {
		while (next= *s++) {
			r.origin.y= *s++;
			r.extent.y= s[next] - r.origin.y;
			while (next > 1) {
				r.origin.x= *s++;
				r.extent.x= *s++-r.origin.x;
				next-= 2;
				bbox.Merge(r);
			}
		}
	}
}

bool operator==(const Region &r1, const Region &r2)
{
	if (r1.cap != r2.cap)
		return FALSE;
	if (r1.buf == 0 && r2.buf == 0)
		return TRUE;
	if (r1.buf == 0 || r2.buf == 0)
		return FALSE;
	return memcmp(r1.buf, r2.buf, r1.cap*sizeof(short)) == 0;
}

void Region::operator=(const Region &r)
{
	if (&r != this) {
		if (r.buf && r.cap > 0) {
			cap= r.cap;
			buf= (short*) Storage::ReAlloc(buf, cap*sizeof(short));
			MemCpy(buf, r.buf, cap*sizeof(short));
			bbox= r.bbox;
		} else {
			cap= 0;
			SafeDelete(buf);
			bbox= gRect0;
		}
	}
}

Region RegionOp(RegionOpcode code, const Region &r1, const Region &r2)
{
	short *shape1= r1.buf, *shape2= r2.buf;

	switch (code) {
	case eRgnCopy:
		return r2;
	case eRgnUnion:
		if (shape1 == 0 && shape2 == 0)
			return Region();
		if (shape1 == 0)
			return Region(r2);
		if (shape2 == 0)
			return Region(r1);
		break;
	case eRgnDifference:
		if (shape1 == 0)
			return Region();
		if (shape2 == 0)
			return Region(r1);
		break;
	case eRgnRevDifference:
		if (shape2 == 0)
			return Region();
		if (shape1 == 0)
			return Region(r2);
		break;
	case eRgnIntersection:
		if (shape1 == 0 || shape2 == 0)
			return Region();
		break;
	}

	short buf[10000];
	register short *ss= buf, *x1, *x2;
	short *fixup, *oldss= 0;
	int y, len, oldlen= -1, l1= 0, l2= 0, tl1, tl2, test;

	for (;;) {
		tl1= *shape1;
		tl2= *shape2;

		if (tl1 == 0 && tl2 == 0)
			break;
		
		if (tl1 == 0)
			test= 1;
		else if (tl2 == 0)
			test= -1;
		else
			test= shape1[1]-shape2[1];
		
		if (test <= 0) {
			y= shape1[1];
			x1= &shape1[2];
			shape1+= tl1+1;
			l1= tl1-1;
		}
		if (test >= 0) {
			y= shape2[1];
			x2= &shape2[2];
			shape2+= tl2+1;
			l2= tl2-1;
		}
		
		fixup= ss;
		*ss++= 1;
		*ss++= y;
		
		if (l1 == 0 && l2 == 0) {
			;
		} else if (l1 == 0) {
			if (code == eRgnUnion || code == eRgnRevDifference) {
				MemCpy(ss, x2, l2*sizeof(short));
				ss+= l2;
			}
		} else if (l2 == 0) {
			if (code == eRgnUnion || code == eRgnDifference) {
				MemCpy(ss, x1, l1*sizeof(short));
				ss+= l1;
			}
		} else {
			register short *p1= x1, *p2= x2;
			register int xl1= l1, xl2= l2;
			register int x, xold= 0, xflag= 0;
				
			while (xl1 > 0 && xl2 > 0) {
				test= *p1-*p2;
				if (test <= 0) {
					x= *p1++;
					xflag ^= 1;
					xl1--;
				}
				if (test >= 0) {
					x= *p2++;
					xflag ^= 2;
					xl2--;
				}
				if (xflag == code || xold == code)
					*ss++= x;
				xold= xflag;
			}
		
			if (code == eRgnUnion || code == eRgnDifference) {
				while (xl1-- > 0)
					*ss++= *p1++;
			}
			if (code == eRgnUnion || code == eRgnRevDifference) {
				while (xl2-- > 0)
					*ss++= *p2++;
			}
		}
		len= ss-fixup-2;
		fixup[0]= len+1;
		
		if (len > 0 && len == oldlen && memcmp(&oldss[2], &fixup[2], len*sizeof(short)) == 0) {
			ss= fixup;
		} else {
			oldss= fixup;
			oldlen= len;
		}
	}
	*ss++= 0;

	return Region(buf, ss-buf);
}

Region SymDifference(const Region &r1, const Region &r2)
{
	Region in(Intersection(r1, r2));
	return Union(Difference(r1, in), Difference(r2, in));
}

bool Region::ContainsPoint(const Point &p) const
{
	register short *bp= buf;
	register int s, e, next;

	if (bbox.ContainsPoint(p)) {
		while (next= *bp++) {
			s= *bp++;
			if (p.y >= s && p.y < bp[next]) {
				while (next > 1) {
					s= *bp++;
					e= *bp++;
					if (p.x >= s && p.x < e)
						return TRUE;
					next-= 2;
				}
			} else
				bp+= next-1;
		}
	}
	return FALSE;
}

OStream &Region::PrintOn(OStream &os) const
{
	os << cap SP;
	for (int i= 0; i < cap; i++) {
		os << buf[i] SP;
		if (((i+1) % 10) == 0)
			os << '\n';
	}
	return os NL;
}

IStream &Region::ReadFrom(IStream &is)
{
	SafeDelete(buf);
	is >> cap;
	if (cap > 0) {
		buf= new short[cap];
		for (int i= 0; i < cap; i++)
			is >> buf[i];
		CalcBBox();
	} else
		buf= 0;
	return is;
}

//---- RegionRectIter ----------------------------------------------------------

RegionRectIter::RegionRectIter(const Region &r)
{
	s= r.buf;
	next= 0;
}

bool RegionRectIter::operator()(Rectangle &r)
{
	while (s) {
		if (next > 1) {
			r.origin.x= *s++;
			r.extent.x= *s++-r.origin.x;
			next-= 2;
			return TRUE;
		}
		next= *s++;
		if (next == 0)
			return FALSE;
		r.origin.y= *s++;
		r.extent.y= s[next]-r.origin.y;
	}
	return FALSE;
}

//---- OldRegion ---------------------------------------------------------------

OldRegion::OldRegion()
{
	size= 0;
	rl= new Rectangle[25];
}

OldRegion::OldRegion(const Rectangle &r)
{
	size= 1;
	rl= new Rectangle[size];
	rl[0]= r;
	bbox= r;
}

OldRegion::~OldRegion()
{
	size= 0;
	SafeDelete(rl);
}

Rectangle *OldRegion::GetAt(int ix) const
{
	if (ix >= 0 && ix < size)
		return &rl[ix];
	return 0;
}

void OldRegion::operator=(const OldRegion &r)
{
	SafeDelete(rl);
	size= r.size;
	bbox= r.bbox;
	rl= new Rectangle[size];
	MemCpy(rl, r.rl, sizeof(Rectangle)*size);
}

void OldRegion::Merge(const Rectangle &r)
{
	if (size == 0) {
		rl[size++]= bbox= r;
		return;
	}
	if (r.ContainsRect(bbox)) {
		size= 0;
		rl[size++]= bbox= r;
		return;
	}
	bbox.Merge(r);
	for (int i= 0; i < size; i++) {
		if (rl[i].ContainsRect(r))
			return;
		if (r.ContainsRect(rl[i])) {
			rl[i]= r;
			return;
		}
		if (r.Intersects(rl[i])) {
			rl[i].Merge(r);
			return;
		}
	}
	if (size >= 20)
		rl[size-1].Merge(r);
	else
		rl[size++]= r;
}

